package com.utils;

import be.ac.ulg.montefiore.run.jahmm.Hmm;
import be.ac.ulg.montefiore.run.jahmm.Opdf;
import be.ac.ulg.montefiore.run.jahmm.OpdfGaussian;
import be.ac.ulg.montefiore.run.jahmm.apps.cli.CommandLineArguments;
import be.ac.ulg.montefiore.run.jahmm.apps.cli.Types;
import be.ac.ulg.montefiore.run.jahmm.apps.cli.WrongArgumentsException;
import be.ac.ulg.montefiore.run.jahmm.io.FileFormatException;
import be.ac.ulg.montefiore.run.jahmm.io.HmmReader;
import be.ac.ulg.montefiore.run.jahmm.io.OpdfReader;
import junit.framework.TestCase;

import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

/**
 * Created with IntelliJ IDEA.
 * User: bigmar
 * Date: 12/21/14
 * Time: 2:19 PM
 * To change this template use File | Settings | File Templates.
 */
public class HmmUtils
{
    /**
     * Compare 2 HMMs
     * @param expected
     * @param actual
     */
    public static void compareHmms(Hmm expected, Hmm actual)
    {

        TestCase.assertEquals(expected.nbStates(), actual.nbStates());

        for (int stateIndex=0; stateIndex<expected.nbStates(); ++stateIndex)
        {
            OpdfGaussian expectedGaussian=(OpdfGaussian)expected.getOpdf(stateIndex);

            OpdfGaussian actualGaussian=(OpdfGaussian)actual.getOpdf(stateIndex);

            compareTwoNumbers(expected.getPi(stateIndex), actual.getPi(stateIndex));
            compareTwoNumbers(expected.getPi(stateIndex), actual.getPi(stateIndex));
            compareTwoNumbers(expectedGaussian.mean(), actualGaussian.mean());
            compareTwoNumbers(expectedGaussian.variance(), actualGaussian.variance());

            for (int i=0; i<actual.nbStates(); ++i)
                compareTwoNumbers(expected.getAij(stateIndex, i), actual.getAij(stateIndex, i));
        }
    }
    private static void compareTwoNumbers(double expected, double actual)
    {
        double dif=Math.abs(expected-actual);
        double difRatio=dif/Math.max(Math.abs(expected), 1);

        TestCase.assertTrue("Expected is "+expected+ " actual is "+actual, difRatio<Constants.allowDifRatio);
    }
    public static void orderHmm(Hmm hmm)
    {
        for (int i=0; i<hmm.nbStates(); ++i)
        {
            double firstMean=((OpdfGaussian)hmm.getOpdf(i)).mean();
            for (int j=i+1; j<hmm.nbStates(); ++j)
            {
                double secMean=((OpdfGaussian)hmm.getOpdf(j)).mean();

                if (firstMean>secMean)
                {
                    // replace pi
                    double tmp=hmm.getPi(i);
                    hmm.setPi(i, hmm.getPi(j));
                    hmm.setPi(j, tmp);

                    // replace Gaussian
                    Opdf opdf=hmm.getOpdf(i);
                    hmm.setOpdf(i, hmm.getOpdf(j));
                    hmm.setOpdf(j, opdf);

                    // Replace row in A matrix
                    for (int k=0; k<hmm.nbStates(); ++k)
                    {
                        tmp=hmm.getAij(i,k);
                        hmm.setAij(i,k, hmm.getAij(j,k));
                        hmm.setAij(j, k, tmp);
                    }

                    // Replace column in A matrix
                    for (int k=0; k<hmm.nbStates(); ++k)
                    {
                        tmp=hmm.getAij(k,i);
                        hmm.setAij(k,i, hmm.getAij(k,j));
                        hmm.setAij(k, j, tmp);
                    }
                    firstMean=secMean;
                }

            }
        }

    }

    /**
     * Print HMM
     * @param hmm
     */
    public static void printHmm(Hmm hmm)
    {
        for (int modelIndex=0; modelIndex<hmm.nbStates(); ++modelIndex)
        {
            OpdfGaussian opdf=(OpdfGaussian)hmm.getOpdf(modelIndex);


            System.out.println("State"+(modelIndex+1));
            System.out.println("    Mixture=    " + hmm.getPi(modelIndex));
            System.out.println("    E=          " + opdf.mean());
            System.out.println("    V=          "+opdf.variance());
            System.out.print("    A=          ");
            for (int i=0; i<hmm.nbStates(); ++i)
                System.out.print((double)(Math.round(100*hmm.getAij(modelIndex,i)))/100+",");
            System.out.println();
        }
    }
    public static Hmm readHmm(String hmmFileName) throws IOException, WrongArgumentsException, FileFormatException {
        CommandLineArguments.Arguments.OPDF.set("gaussian");
        CommandLineArguments.Arguments.OPDF.setIsSet(true);
        Reader hmmReader=new FileReader(hmmFileName);
        OpdfReader opdfReader = Types.relatedObjs().opdfReader();
        Hmm hmm= HmmReader.read(hmmReader, opdfReader);
        return hmm;
    }
}
